package player;

import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import model.Player;
import model.Preference;
import model.Settings;
import model.inventory.InventoryPlayer;
import net.HttpRequestFacade;

import org.apache.commons.io.FileUtils;

import platform.Platform;
import platform.cmdexecutor.PlatformCommandExecutor;
import scripts.PermissionSetter;
import sync.BoxInfoSynchronizer;
import sync.BoxSystemInfo;
import threads.DownloadQueue;
import threads.LogRemover;
import threads.PlayerMonitor;
import threads.Updater;
import util.EmailSending;
import util.LoggingUtils;
import util.PlayerUtilities;
import view.PlayerCanvas;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;

public class Test {

	private static final String className = Test.class.getName();
	
	public static void runPlayer() throws InterruptedException, InvocationTargetException {
		
		 long heapSize = Runtime.getRuntime().totalMemory();
		 
		//Print the jvm heap size.
	    System.out.println("Heap Size = " + heapSize);
	        
		final String classMethod = className + ".runPlayer()";
		LoggingUtils.logStart(classMethod);
		
		// System.out.println(System.getProperties());
		LoggingUtils.log(Level.SEVERE, "Player started with logging enabled " + classMethod);
		
		try {
			//Exception NullPointerException = null;
			//throw NullPointerException;
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			e.printStackTrace();
			LoggingUtils.log(Level.SEVERE, "Can not load the look and feel "+e.getMessage(),e);
		}

		if(Preference.getInstance().isCheckUpdates())
		{
			Thread t = new Thread(new Runnable() {
				@Override
				public void run() {
					new BoxInfoSynchronizer(new BoxSystemInfo()).sendBoxInfo();
					// System.out.println(new BoxInfoSynchronizer(new
					// BoxSystemInfo()).getRequestString());
				}
			});
		
		
			t.setName("box info sender thread");
			LoggingUtils.logStart(classMethod, t);
			//System.out.println("=== BoxInfo Initializing: "+t.getName() + '=');
			t.start();
		
		}

		// read the preference
		Preference pref = Preference.getInstance();
		
		//Random rand = new Random();
		int minutes = 50;
		
		PlayerStartEmail(minutes);	
		// pref.setBoxID(319);
		// pref.setZeroPauseTime(true);
		// pref.setClearContentOnStartup(true);
		// pref.setResendContentOnStartup(true);
		
		//NullPointerException en=null;
		//LoggingUtils.log(Level.SEVERE, "Exception testing ",en.fillInStackTrace());
		
		// linux upgrade fix
		if (Platform.getPlatform() == Platform.LINUX) {
			try {
				File restartPlayerScript = new File("RestartPlayer.sh");
				File installLinuxScript = new File("install_linuxplayer.sh");
				PermissionSetter setter = new PermissionSetter();
				if (restartPlayerScript.exists()) {
					FileUtils.copyFile(restartPlayerScript, new File("../RestartPlayer.sh"));
					setter.givePermission("../RestartPlayer.sh", "a+x");
					restartPlayerScript.deleteOnExit();
				}
				if (installLinuxScript.exists()) {
					FileUtils.copyFile(installLinuxScript, new File("../install_linuxplayer.sh"));
					setter.givePermission("../install_linuxplayer.sh", "a+x");
					installLinuxScript.deleteOnExit();
					Player.getInstance().resetPlayer();
				}
			} catch (IOException e) {
				LoggingUtils.log(Level.SEVERE, "System Error in " + classMethod + ": "
						+ "Cannot process Linux restartPlayerScript in " + classMethod,  e);
				//e.printStackTrace();
			}
		}
		else if (Platform.getPlatform() == Platform.MAC) {
		}

		if (pref.isClearContentOnStartup()) {
			LoggingUtils.log(Level.SEVERE, "Player is cleared on startup " + className + "runPlayer().");
			System.out.println("clear player on startup");

			// PlayerUtilities.recursiveDelete("resources"));
			// PlayerUtilities.deleteDir(new File("resources/"));
			// PlayerUtilities.recursiveDelete("resources");
			PlayerUtilities.deleteResourcesFolder();
			pref.setClearContentOnStartup(false);
		}
		// PlayerUtilities.saveStringToFile("output.txt",
		// PlayerUtilities.getStringFromFile(Settings.PREFERENCES_XML));

		// make player restart every day
		
		Preference.getInstance().getRestartDuration(Preference.getInstance().getScheduleday(), Preference.getInstance().getScheduleTime());
		/* Scheduling thread to restart */
		
		/*
		LoggingUtils.log(Level.SEVERE, "Player is scheduled to restart at 3:" + minutes + ":0");
		TimerTask scheduleRestart = new TimerTask(3,minutes,0);
		scheduleRestart.start();
		*/
		
		
		if (pref.isResendContentOnStartup()) {
			HttpRequestFacade.resendContent();
			pref.setResendContentOnStartup(false);
		}

		PlayerUtilities.createDirectories();
		// move the cursor to the middle of the player, avoid top menubar on mac
		PlayerUtilities.moveMouse(PlayerUtilities.scale(pref.getPlayerWidth() / 2, pref.getScale()),
				PlayerUtilities.scale(pref.getPlayerHeight() / 2, pref.getScale()));

		final Player p = Player.getInstance();

		Thread startupDownloader = new Thread(new Runnable() {

			@Override
			public void run() {
				p.downloadContents();
			}
		});
		
		startupDownloader.setName("startup content downloader");
		LoggingUtils.logStart(classMethod, startupDownloader);
		//System.out.println("=== Thread Name, starting: "+startupDownloader.getName() + '=');
		startupDownloader.start();

		LoggingUtils.logStart("invokeAndWait of Runnable to create and show PlayerCanvas in " + classMethod + ".");
		
		if(PlayerUtilities.isBrowserSupported())
		{
			NativeInterface.open();
		}
		
		/*Thread swtThread = new Thread(new Runnable() {

			@Override
			public void run() {
				PlayerCanvas c = new PlayerCanvas(p);
				c.setVisible(true);
				System.out.println("== Canvas is opening =");
			}
		});
		
		swtThread.setName("Swt thread");
		swtThread.start();*/
		
		//SwingUtilities.invokeLater(
				//invokeAndWait
		
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				PlayerCanvas c = new PlayerCanvas(p);
				c.setVisible(true);
				System.out.println("== Canvas is opening =");
				// SwingUtilities.isEventDispatchThread());
			}
		});
		
		
		//NativeInterface.removeNativeInterfaceListener();
		
		Runtime.getRuntime().addShutdownHook(new Thread() {
			// This method is called during shutdown
			@Override
			public void run() {
				LoggingUtils.logStart("run() of addShutdownHook(Thread) in " + className + ".runPlayer() - Saving player settings before shutdown");
				System.out.println("saving player settings before shutting down.");
				InventoryPlayer.getInstance().saveToXML();
				Player.getInstance().save();
				Preference.getInstance().save();
				//RBB Start
				if (! PlatformCommandExecutor.quitPlayerShutdown) {
					// quitPlayerShutdown is only set to true in PlatformCommandExecutor.quitPlayer().
					// If an abnormal shutdown (crash) occurred, then quitPlayerShutdown would be false.
					// Now, we need to restartPlayer to ensure the menubox is gracefully restarted 
					// after an unexpected crash.
					HttpRequestFacade.postPlayerException("Critical", "Unexpected shutdown (quitPlayerShutdown is false) in "
							+ "run() of addShutdownHook(Thread) in " ,"Restart");
					
					LoggingUtils.log(Level.SEVERE, "Unexpected shutdown (quitPlayerShutdown is false) in "
							+ "run() of addShutdownHook(Thread) in " + classMethod);
					
					//PlatformObjectFactory.getInstance().getCommandExecutor().restartPlayer();
				}
				else {
					// Reset flag to false to ensure that an unexpected shutdown (crash) will be handled
					// by the if Not condition above.
					PlatformCommandExecutor.quitPlayerShutdown = false;
				}
				//RBB End
			}
		});
		// playRunnable = new PlayerThread(player);
		Thread playerThread = new Thread(p);
		playerThread.setName("Player Thread");
		LoggingUtils.logStart(classMethod, playerThread);
		//System.out.println("=== Thread Name, starting: "+playerThread.getName() + '=');
		playerThread.start();

		Thread downloaderThread = new Thread(DownloadQueue.getInstance());
		downloaderThread.setName("Download Queue");
		LoggingUtils.logStart(classMethod, downloaderThread);
		//System.out.println("=== Thread Name, starting: "+downloaderThread.getName() + '=');
		downloaderThread.start();

		// Thread checker = new Thread(new Checker(playerThread));
		// checker.setName("Checker thread");
		// checker.start();
		PlayerMonitor monitor = new PlayerMonitor();
		monitor.addThread(playerThread);
		monitor.addThread(downloaderThread);

		if (Preference.getInstance().isCheckUpdates()) {
			// Timer timer = new Timer();
			// timer.scheduleAtFixedRate(new Updater(player), 0,
			// 1000*Preference.getInstance().getUpdateCheckerFrequency());
			Thread updaterThread = new Thread(new Updater(p));
			// Thread updaterThread = new Thread(new UpdateTester(player));
			updaterThread.setName("Updater Thread");
			LoggingUtils.logStart(classMethod, updaterThread);
			updaterThread.start();
			//System.out.println("=== Thread Name, starting if check update is true: "+updaterThread.getName() + '=');
			monitor.addThread(updaterThread);
		}
		
		//System.out.println("*******************************************************************************");
		Thread monitorThread = new Thread(monitor);
		LoggingUtils.logStart(classMethod, monitorThread);

		monitorThread.start();
		//LoggingUtils.log(Level.WARNING, "End of Test.run().");
		
		/*WeatherThread alarmClock = new WeatherThread(16, 49, 0);
        alarmClock.start();*/
		
		Thread logRemoverThread = new Thread(new LogRemover(5, 1000*60*60*24));
		logRemoverThread.setName("Log Remover Thread.");
		//System.out.println("=== Thread Name, starting: "+logRemoverThread.getName() + '=');
		logRemoverThread.start();
		
		if (Preference.getInstance().isCheckUpdates()) {
			sentSavedCommandToServer();
		}
	}

	public static void testEvents() {
		JFrame frame = new JFrame("test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setPreferredSize(new Dimension(800, 600));
		JPanel p = new JPanel();
		p.addPropertyChangeListener(new PropertyChangeListener() {

			@Override
			public void propertyChange(PropertyChangeEvent evt) {
				// TODO Auto-generated method stub
				System.out.println("property has changed.");
				System.out.println(SwingUtilities.isEventDispatchThread());
			}
		});

		frame.setContentPane(p);
		frame.setVisible(true);
		p.setSize(100, 100);
	}
	
	public static void PlayerStartEmail(int minutes)
	{
		if(Preference.getInstance().getBoxID() != 0 && Preference.getInstance().getBoxID() != 274)
		{
			final int min = minutes;
			System.out.println("Sending start up email");

			Thread emailingThread = new Thread(new Runnable() {

				@Override
				public void run() {
					String EmailBody = EmailSending.ProcessEmailBody("Starting player",	"Starting player, player scheduled to restart every day at 3:"+String.valueOf(min)+":0","Starting player from main method");
					util.EmailSending.getInstance().send(Settings.TO_USER, "Starting:DMB:" +"BoxID="+Preference.getInstance().getBoxID(), EmailBody);
				}
			});
			emailingThread.setName("Startup email sending");
			emailingThread.start();

		}
	}
	
	public static void sentSavedCommandToServer()
	{
		try
		{
			File file = new File(Settings.REPLY_TO_FILENAME);
			if(file.exists())
			{
				final String string = PlayerUtilities.getStringFromFile(Settings.REPLY_TO_FILENAME).trim();
				if(!string.equals(""))
				{
					Thread jsonThread = new Thread(new Runnable() {
						@Override
						public void run() {
							HttpRequestFacade.postContentUpdateConfirmation(string);
						}
					});
					jsonThread.setName("PlayerUpdateConfirmation");
					jsonThread.start();
				}
				file.delete();
			}
		}
		catch(Exception e)
		{
			LoggingUtils.log(Level.SEVERE, " sentSavedCommandToServer() from main() "+e.getMessage(), e);
		}
	}

	public static void main(String[] args) {
		
/*		long heapSize = Runtime.getRuntime().totalMemory();
		long maxSize = Runtime.getRuntime().maxMemory();
		long freesize = Runtime.getRuntime().freeMemory();
		
		heapSize = (heapSize/1024)/1024;
		maxSize = (maxSize/1024)/1024;
		freesize = (freesize/1024)/1024;
		
		
		System.out.println("heapSize = " + heapSize + "MB");
		System.out.println("maxSize = " + maxSize + "MB");
		System.out.println("freesize = " + freesize + "MB");
		
		System.exit(1);*/
		
		
		try {
			runPlayer();
		} catch (Throwable e) {
			LoggingUtils.log(Level.SEVERE, "System Error in call to runPlayer() in " + className + ".main(...)." 
					+ " . restartPlayer() called.", e);
			//e.printStackTrace();
			
			//HttpRequestFacade.postPlayerException("Critical", "Player.Test() method \n"+EmailSending.FormatException(e),"Restart");
			//PlatformObjectFactory.getInstance().getCommandExecutor().restartPlayer();
		}
		
		try
		{
			if (Platform.getPlatform() == Platform.MAC)
			{
				NativeInterface.runEventPump();
				NativeInterface.close();
			}
		}
		catch(NullPointerException n)
		{
			System.out.println("Null pointer exception");
		}
		catch(Exception e)
		{
			System.out.println("Exception in event interface!");
		}
		
		
		// test();
	}

}
